/*****************************************************************************
 * xyuv.c: a SDL yuv 420 planer viewer.
 *****************************************************************************
 * Copyright (C) 2004 Laurent Aimar <fenrir@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
 *****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include <SDL/SDL.h>

#define YUV_MAX 20
#define SDL_TITLE "xyuv: %s - %d/%d - %.2ffps"
typedef struct
{
  /* globals */
  int i_width;
  int i_height;
  int i_frame_size;
  int i_frame;
  int i_frames;
  float f_fps;

  float f_y;

  int b_pause;
  int b_grid;
  int b_split;
  int b_diff;
  int i_join;

  /* Constructed picture */
  int i_wall_width;             /* in picture count */

  /* YUV files */
  int i_yuv;
  struct
  {
    char *name;
    FILE *f;                    /* handles */
    int i_frames;               /* frames count */

    /* Position in the whole picture */
    int x, y;
  } yuv[YUV_MAX];

  /* SDL */
  int i_sdl_width;
  int i_sdl_height;

  int i_display_width;
  int i_display_height;
  char *title;

  SDL_Surface *screen;
  SDL_Overlay *overlay;

  /* */
  uint8_t *pic;

} xyuv_t;

xyuv_t xyuv = {
  .i_width = 0,
  .i_height = 0,
  .i_frame = 1,
  .i_frames = 0,
  .f_fps = 25.0,
  .f_y = 0.0,
  .i_wall_width = 0,

  .i_yuv = 0,

  .b_pause = 0,
  .b_split = 0,
  .b_diff = 0,
  .i_join = -1,

  .title = NULL,
  .pic = NULL,
};

static void
help (void)
{
  fprintf (stderr,
           "Syntax: xyuv [options] file [file2 ...]\n"
           "\n"
           "      --help                  Print this help\n"
           "\n"
           "  -s, --size <WIDTHxHEIGHT>   Set input size\n"
           "  -w, --width <integer>       Set width\n"
           "  -h, --height <integer>      Set height\n"
           "\n"
           "  -S, --split                 Show splited Y/U/V planes\n"
           "  -d, --diff                  Show difference (only 2 files) in split mode\n"
           "  -j, --joint <integer>\n" "\n" "  -y <float>                  Set Y factor\n" "\n" "  -g, --grid                  Show a grid (macroblock 16x16)\n" "  -W <integer>                Set wall width (in picture count)\n" "  -f, --fps <float>           Set fps\n" "\n");
}

static void xyuv_count_frames (xyuv_t * xyuv);
static void xyuv_detect (int *pi_width, int *pi_height);
static void xyuv_display (xyuv_t * xyuv, int i_frame);

int
main (int argc, char **argv)
{
  int i;

  /* Parse commande line */
  for (i = 1; i < argc; i++)
  {
    if (!strcasecmp (argv[i], "--help"))
    {
      help ();
      return 0;
    }
    if (!strcmp (argv[i], "-d") || !strcasecmp (argv[i], "--diff"))
    {
      xyuv.b_diff = 1;
    }
    else if (!strcmp (argv[i], "-S") || !strcasecmp (argv[i], "--split"))
    {
      xyuv.b_split = 1;
    }
    else if (!strcmp (argv[i], "-f") || !strcasecmp (argv[i], "--fps"))
    {
      if (i >= argc - 1)
        goto err_missing_arg;
      xyuv.f_fps = atof (argv[++i]);
    }
    else if (!strcmp (argv[i], "-h") || !strcasecmp (argv[i], "--height"))
    {
      if (i >= argc - 1)
        goto err_missing_arg;
      xyuv.i_height = atoi (argv[++i]);
    }
    else if (!strcmp (argv[i], "-w") || !strcasecmp (argv[i], "--width"))
    {
      if (i >= argc - 1)
        goto err_missing_arg;
      xyuv.i_width = atoi (argv[++i]);
    }
    else if (!strcmp (argv[i], "-s") || !strcasecmp (argv[i], "--size"))
    {
      char *p;

      if (i >= argc - 1)
        goto err_missing_arg;

      xyuv.i_width = strtol (argv[++i], &p, 0);
      p++;
      xyuv.i_height = atoi (p);
    }
    else if (!strcmp (argv[i], "-W"))
    {
      if (i >= argc - 1)
        goto err_missing_arg;
      xyuv.i_wall_width = atoi (argv[++i]);
    }
    else if (!strcmp (argv[i], "-y"))
    {
      if (i >= argc - 1)
        goto err_missing_arg;
      xyuv.f_y = atof (argv[++i]);
    }
    else if (!strcmp (argv[i], "-j") || !strcasecmp (argv[i], "--join"))
    {
      if (i >= argc - 1)
        goto err_missing_arg;
      xyuv.i_join = atoi (argv[++i]);
    }
    else if (!strcmp (argv[i], "-g") || !strcasecmp (argv[i], "--grid"))
    {
      xyuv.b_grid = 1;
    }
    else
    {
      FILE *f = fopen (argv[i], "rb");
      if (!f)
      {
        fprintf (stderr, "cannot open YUV %s\n", argv[i]);
      }
      else
      {
        xyuv.yuv[xyuv.i_yuv].name = strdup (argv[i]);
        xyuv.yuv[xyuv.i_yuv].f = f;
        xyuv.yuv[xyuv.i_yuv].i_frames = 0;

        xyuv.i_yuv++;
      }
    }
  }

  if (xyuv.i_yuv == 0)
  {
    fprintf (stderr, "no file to display\n");
    return -1;
  }
  if (xyuv.i_width == 0 || xyuv.i_height == 0)
  {
    char *psz = xyuv.yuv[0].name;
    char *num;
    char *x;
    /* See if we find widthxheight in the file name */
    for (;;)
    {
      if (!(x = strchr (psz + 1, 'x')))
      {
        break;
      }
      num = x;
      while (num > psz && num[-1] >= '0' && num[-1] <= '9')
        num--;

      if (num != x && x[1] >= '0' && x[1] <= '9')
      {
        xyuv.i_width = atoi (num);
        xyuv.i_height = atoi (x + 1);
        break;
      }
      psz = x;
    }
    fprintf (stderr, "file name gives %dx%d\n", xyuv.i_width, xyuv.i_height);
  }

  if (xyuv.i_width == 0 || xyuv.i_height == 0)
  {
    xyuv_detect (&xyuv.i_width, &xyuv.i_height);
  }

  if (xyuv.i_width == 0 || xyuv.i_height == 0)
  {
    fprintf (stderr, "invalid or missing frames size\n");
    return -1;
  }
  if (xyuv.b_diff && xyuv.i_yuv != 2)
  {
    fprintf (stderr, "--diff works only with 2 files\n");
    return -1;
  }
  if ((xyuv.i_join == 0 || xyuv.i_join >= xyuv.i_width) && xyuv.i_yuv != 2)
  {
    fprintf (stderr, "--join woeks only with two files and range is [1, width-1]\n");
    return -1;
  }
  if (xyuv.i_join % 2 != 0)
  {
    if (xyuv.i_join + 1 < xyuv.i_width)
      xyuv.i_join++;
    else
      xyuv.i_join--;
  }

  /* Now check frames */
  fprintf (stderr, "displaying :\n");
  xyuv.i_frame_size = 3 * xyuv.i_width * xyuv.i_height / 2;
  xyuv_count_frames (&xyuv);
  for (i = 0; i < xyuv.i_yuv; i++)
  {
    fprintf (stderr, " - '%s' : %d frames\n", xyuv.yuv[i].name, xyuv.yuv[i].i_frames);
  }

  if (xyuv.i_frames == 0)
  {
    fprintf (stderr, "no frames to display\n");
  }

  xyuv.pic = malloc (xyuv.i_frame_size);

  /* calculate SDL view */
  if (xyuv.i_wall_width > xyuv.i_yuv)
  {
    xyuv.i_wall_width = xyuv.i_yuv;
  }
  if (xyuv.i_wall_width == 0)
  {
    while (xyuv.i_wall_width < xyuv.i_yuv && xyuv.i_wall_width * xyuv.i_wall_width < xyuv.i_yuv)
    {
      xyuv.i_wall_width++;
    }
  }

  for (i = 0; i < xyuv.i_yuv; i++)
  {
    if (xyuv.b_diff || xyuv.i_join > 0)
    {
      xyuv.yuv[i].x = 0;
      xyuv.yuv[i].y = 0;
    }
    else if (xyuv.b_split)
    {
      xyuv.yuv[i].x = (i % xyuv.i_wall_width) * 3 * xyuv.i_width / 2;
      xyuv.yuv[i].y = (i / xyuv.i_wall_width) * xyuv.i_height;
    }
    else
    {
      xyuv.yuv[i].x = (i % xyuv.i_wall_width) * xyuv.i_width;
      xyuv.yuv[i].y = (i / xyuv.i_wall_width) * xyuv.i_height;
    }
  }
  if (xyuv.b_diff)
  {
    xyuv.i_sdl_width = 3 * xyuv.i_width / 2;
    xyuv.i_sdl_height = xyuv.i_height;
  }
  else if (xyuv.i_join > 0)
  {
    xyuv.i_sdl_width = xyuv.i_width;
    xyuv.i_sdl_height = xyuv.i_height;
  }
  else if (xyuv.b_split)
  {
    xyuv.i_sdl_width = xyuv.i_wall_width * 3 * xyuv.i_width / 2;
    xyuv.i_sdl_height = xyuv.i_height * ((xyuv.i_yuv + xyuv.i_wall_width - 1) / xyuv.i_wall_width);
  }
  else
  {
    xyuv.i_sdl_width = xyuv.i_wall_width * xyuv.i_width;
    xyuv.i_sdl_height = xyuv.i_height * ((xyuv.i_yuv + xyuv.i_wall_width - 1) / xyuv.i_wall_width);
  }
  xyuv.i_display_width = xyuv.i_sdl_width;
  xyuv.i_display_height = xyuv.i_sdl_height;

  /* Open SDL */
  if (SDL_Init (SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE | SDL_INIT_VIDEO))
  {
    fprintf (stderr, "cannot init SDL\n");
    return -1;
  }

  SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, 100);
  SDL_EventState (SDL_KEYUP, SDL_IGNORE);

  xyuv.screen = SDL_SetVideoMode (xyuv.i_sdl_width, xyuv.i_sdl_height, 0, SDL_HWSURFACE | SDL_RESIZABLE | SDL_ASYNCBLIT | SDL_HWACCEL);
  if (xyuv.screen == NULL)
  {
    fprintf (stderr, "SDL_SetVideoMode failed\n");
    return -1;
  }

  SDL_LockSurface (xyuv.screen);
  xyuv.overlay = SDL_CreateYUVOverlay (xyuv.i_sdl_width, xyuv.i_sdl_height, SDL_YV12_OVERLAY, xyuv.screen);
  /* reset with black */
  memset (xyuv.overlay->pixels[0], 0, xyuv.overlay->pitches[0] * xyuv.i_sdl_height);
  memset (xyuv.overlay->pixels[1], 128, xyuv.overlay->pitches[1] * xyuv.i_sdl_height / 2);
  memset (xyuv.overlay->pixels[2], 128, xyuv.overlay->pitches[2] * xyuv.i_sdl_height / 2);
  SDL_UnlockSurface (xyuv.screen);

  if (xyuv.overlay == NULL)
  {
    fprintf (stderr, "recon: SDL_CreateYUVOverlay failed\n");
    return -1;
  }

  for (;;)
  {
    SDL_Event event;
    static int b_fullscreen = 0;
    int64_t i_start = SDL_GetTicks ();
    int i_wait;

    if (!xyuv.b_pause)
    {
      xyuv_display (&xyuv, xyuv.i_frame);
    }

    for (;;)
    {
      int b_refresh = 0;
      while (SDL_PollEvent (&event))
      {
        switch (event.type)
        {
        case SDL_QUIT:
          if (b_fullscreen)
            SDL_WM_ToggleFullScreen (xyuv.screen);
          exit (1);

        case SDL_KEYDOWN:
          switch (event.key.keysym.sym)
          {
          case SDLK_q:
          case SDLK_ESCAPE:
            if (b_fullscreen)
              SDL_WM_ToggleFullScreen (xyuv.screen);
            exit (1);

          case SDLK_f:
            if (SDL_WM_ToggleFullScreen (xyuv.screen))
              b_fullscreen = 1 - b_fullscreen;
            break;

          case SDLK_g:
            if (xyuv.b_grid)
              xyuv.b_grid = 0;
            else
              xyuv.b_grid = 1;
            if (xyuv.b_pause)
              b_refresh = 1;
            break;

          case SDLK_SPACE:
            if (xyuv.b_pause)
              xyuv.b_pause = 0;
            else
              xyuv.b_pause = 1;
            break;
          case SDLK_LEFT:
            if (xyuv.i_frame > 1)
              xyuv.i_frame--;
            b_refresh = 1;
            break;

          case SDLK_RIGHT:
            if (xyuv.i_frame >= xyuv.i_frames)
              xyuv_count_frames (&xyuv);
            if (xyuv.i_frame < xyuv.i_frames)
              xyuv.i_frame++;
            b_refresh = 1;
            break;

          case SDLK_HOME:
            xyuv.i_frame = 1;
            if (xyuv.b_pause)
              b_refresh = 1;
            break;

          case SDLK_END:
            xyuv_count_frames (&xyuv);
            xyuv.i_frame = xyuv.i_frames;
            b_refresh = 1;
            break;

          case SDLK_UP:
            xyuv.i_frame += xyuv.i_frames / 20;

            if (xyuv.i_frame >= xyuv.i_frames)
              xyuv_count_frames (&xyuv);

            if (xyuv.i_frame > xyuv.i_frames)
              xyuv.i_frame = xyuv.i_frames;
            b_refresh = 1;
            break;

          case SDLK_DOWN:
            xyuv.i_frame -= xyuv.i_frames / 20;
            if (xyuv.i_frame < 1)
              xyuv.i_frame = 1;
            b_refresh = 1;
            break;

          case SDLK_PAGEUP:
            xyuv.i_frame += xyuv.i_frames / 10;

            if (xyuv.i_frame >= xyuv.i_frames)
              xyuv_count_frames (&xyuv);

            if (xyuv.i_frame > xyuv.i_frames)
              xyuv.i_frame = xyuv.i_frames;
            b_refresh = 1;
            break;

          case SDLK_PAGEDOWN:
            xyuv.i_frame -= xyuv.i_frames / 10;
            if (xyuv.i_frame < 1)
              xyuv.i_frame = 1;
            b_refresh = 1;
            break;

          default:
            break;
          }
          break;
        case SDL_VIDEORESIZE:
          xyuv.i_display_width = event.resize.w;
          xyuv.i_display_height = event.resize.h;
          xyuv.screen = SDL_SetVideoMode (xyuv.i_display_width, xyuv.i_display_height, 0, SDL_HWSURFACE | SDL_RESIZABLE | SDL_ASYNCBLIT | SDL_HWACCEL);
          xyuv_display (&xyuv, xyuv.i_frame);
          break;

        default:
          break;
        }
      }
      if (b_refresh)
      {
        xyuv.b_pause = 1;
        xyuv_display (&xyuv, xyuv.i_frame);
      }
      /* wait */
      i_wait = 1000 / xyuv.f_fps - (SDL_GetTicks () - i_start);
      if (i_wait < 0)
        break;
      else if (i_wait > 200)
        SDL_Delay (200);
      else
      {
        SDL_Delay (i_wait);
        break;
      }
    }
    if (!xyuv.b_pause)
    {
      /* next frame */
      if (xyuv.i_frame == xyuv.i_frames)
        xyuv.b_pause = 1;
      else if (xyuv.i_frame < xyuv.i_frames)
        xyuv.i_frame++;
    }
  }


  return 0;

err_missing_arg:
  fprintf (stderr, "missing arg for option=%s\n", argv[i]);
  return -1;
}


static void
xyuv_display (xyuv_t * xyuv, int i_frame)
{
  SDL_Rect rect;
  int i_picture = 0;
  int i;

  if (i_frame > xyuv->i_frames)
    return;

  xyuv->i_frame = i_frame;

  /* Load and copy pictue data */
  for (i = 0; i < xyuv->i_yuv; i++)
  {
    int i_plane;

    fprintf (stderr, "yuv[%d] %d/%d\n", i, i_frame, xyuv->yuv[i].i_frames);
    if (i_frame - 1 >= xyuv->yuv[i].i_frames)
    {
      xyuv_count_frames (xyuv);
      if (i_frame - 1 >= xyuv->yuv[i].i_frames)
        continue;
    }
    i_picture++;

    fseek (xyuv->yuv[i].f, (xyuv->i_frame - 1) * xyuv->i_frame_size, SEEK_SET);
    fread (xyuv->pic, xyuv->i_frame_size, 1, xyuv->yuv[i].f);

    SDL_LockYUVOverlay (xyuv->overlay);

    if (xyuv->b_diff || xyuv->b_split)
    {
      /* Reset UV */
      for (i_plane = 1; i_plane < 3; i_plane++)
      {
        memset (xyuv->overlay->pixels[i_plane], 128, xyuv->overlay->pitches[i_plane] * xyuv->overlay->h / 2);
      }
      /* Show diff in Y plane of overlay */

      for (i_plane = 0; i_plane < 3; i_plane++)
      {
        int div = i_plane == 0 ? 1 : 2;
        uint8_t *src = xyuv->pic;
        uint8_t *dst = xyuv->overlay->pixels[0] + (xyuv->yuv[i].x + xyuv->yuv[i].y * xyuv->overlay->pitches[0]);
        int j;
        if (i_plane == 1)
        {
          src += 5 * xyuv->i_width * xyuv->i_height / 4;
          dst += xyuv->i_width;
        }
        else if (i_plane == 2)
        {
          src += xyuv->i_width * xyuv->i_height;
          dst += xyuv->i_width + xyuv->i_height / 2 * xyuv->overlay->pitches[0];
        }

        for (j = 0; j < xyuv->i_height / div; j++)
        {
          if (i_picture == 1 || xyuv->b_split)
          {
            memcpy (dst, src, xyuv->i_width / div);
          }
          else
          {
            int k;
            for (k = 0; k < xyuv->i_width / div; k++)
            {
              dst[k] = abs (dst[k] - src[k]);
            }
          }
          src += xyuv->i_width / div;
          dst += xyuv->overlay->pitches[0];
        }
      }
    }
    else
    {
      for (i_plane = 0; i_plane < 3; i_plane++)
      {
        int div = i_plane == 0 ? 1 : 2;
        uint8_t *src = xyuv->pic;
        uint8_t *dst = xyuv->overlay->pixels[i_plane] + ((xyuv->yuv[i].x + xyuv->yuv[i].y * xyuv->overlay->pitches[i_plane]) / div);
        int w = xyuv->i_width / div;
        int j;

        if (i_plane == 1)
        {
          src += 5 * xyuv->i_width * xyuv->i_height / 4;
        }
        else if (i_plane == 2)
        {
          src += xyuv->i_width * xyuv->i_height;
        }
        if (xyuv->i_join > 0)
        {
          if (i_picture > 1)
          {
            src += xyuv->i_join / div;
            dst += xyuv->i_join / div;
            w = (xyuv->i_width - xyuv->i_join) / div;
          }
          else
          {
            w = xyuv->i_join / div;
          }
        }

        for (j = 0; j < xyuv->i_height / div; j++)
        {
          memcpy (dst, src, w);
          src += xyuv->i_width / div;
          dst += xyuv->overlay->pitches[i_plane];
        }
      }
    }

    SDL_UnlockYUVOverlay (xyuv->overlay);
  }

  if (xyuv->f_y != 0.0)
  {
    uint8_t *pix = xyuv->overlay->pixels[0];
    int j;

    for (j = 0; j < xyuv->i_sdl_height; j++)
    {
      int k;
      for (k = 0; k < xyuv->i_sdl_width; k++)
      {
        int v = pix[k] * xyuv->f_y;
        if (v > 255)
          pix[k] = 255;
        else if (v < 0)
          pix[k] = 0;
        else
          pix[k] = v;
      }
      pix += xyuv->overlay->pitches[0];
    }
  }
  if (xyuv->b_grid)
  {
    int x, y;

    for (y = 0; y < xyuv->i_sdl_height; y += 4)
    {
      uint8_t *p = xyuv->overlay->pixels[0] + y * xyuv->overlay->pitches[0];
      for (x = 0; x < xyuv->i_sdl_width; x += 4)
      {
        if (x % 16 == 0 || y % 16 == 0)
          p[x] = 0;
      }
    }
  }

  /* Update display */
  rect.x = 0;
  rect.y = 0;
  rect.w = xyuv->i_display_width;
  rect.h = xyuv->i_display_height;
  SDL_DisplayYUVOverlay (xyuv->overlay, &rect);

  /* Display title */
  if (xyuv->title)
    free (xyuv->title);
  asprintf (&xyuv->title, SDL_TITLE, xyuv->yuv[0].name, xyuv->i_frame, xyuv->i_frames, xyuv->f_fps);
  SDL_WM_SetCaption (xyuv->title, "");
}

static void
xyuv_count_frames (xyuv_t * xyuv)
{
  int i;

  xyuv->i_frames = 0;
  if (xyuv->i_frame_size <= 0)
    return;

  for (i = 0; i < xyuv->i_yuv; i++)
  {
    /* Beurk but avoid using fstat */
    fseek (xyuv->yuv[i].f, 0, SEEK_END);

    xyuv->yuv[i].i_frames = ftell (xyuv->yuv[i].f) / xyuv->i_frame_size;
    fprintf (stderr, "count (%d) -> %d\n", i, xyuv->yuv[i].i_frames);

    fseek (xyuv->yuv[i].f, 0, SEEK_SET);

    if (xyuv->i_frames < xyuv->yuv[i].i_frames)
      xyuv->i_frames = xyuv->yuv[i].i_frames;
  }
}

static inline int
ssd (int a)
{
  return a * a;
}

static void
xyuv_detect (int *pi_width, int *pi_height)
{
  static const int pi_size[][2] = {
    {128, 96},
    {160, 120},
    {320, 244},
    {320, 288},

    /* PAL */
    {176, 144},                 // QCIF
    {352, 288},                 // CIF
    {352, 576},                 // 1/2 D1
    {480, 576},                 // 2/3 D1
    {544, 576},
    {640, 576},                 // VGA
    {704, 576},                 // D1
    {720, 576},                 // D1

    /* NTSC */
    {176, 112},                 // QCIF
    {320, 240},                 // MPEG I
    {352, 240},                 // CIF
    {352, 480},                 // 1/2 D1
    {480, 480},                 // 2/3 D1
    {544, 480},
    {640, 480},                 // VGA
    {704, 480},                 // D1
    {720, 480},                 // D1

    /* */
    {0, 0},
  };
  int i_max;
  int i_size_max;
  uint8_t *pic;
  int i;

  *pi_width = 0;
  *pi_height = 0;

  /* Compute size max */
  for (i_max = 0, i_size_max = 0; pi_size[i_max][0] != 0 && pi_size[i_max][1] != 0; i_max++)
  {
    int s = pi_size[i_max][0] * pi_size[i_max][1] * 3 / 2;

    if (i_size_max < s)
      i_size_max = s;
  }

  /* Temporary buffer */
  i_size_max *= 3;
  pic = malloc (i_size_max);

  fprintf (stderr, "guessing size for:\n");
  for (i = 0; i < xyuv.i_yuv; i++)
  {
    int j;
    int i_read;
    double dbest = 255 * 255;
    int i_best = i_max;
    int64_t t;

    fprintf (stderr, " - %s\n", xyuv.yuv[i].name);

    i_read = fread (pic, 1, i_size_max, xyuv.yuv[i].f);
    if (i_read < 0)
      continue;

    /* Check if file size is at least compatible with one format
     * (if not, ignore file size)*/
    fseek (xyuv.yuv[i].f, 0, SEEK_END);
    t = ftell (xyuv.yuv[i].f);
    fseek (xyuv.yuv[i].f, 0, SEEK_SET);
    for (j = 0; j < i_max; j++)
    {
      const int w = pi_size[j][0];
      const int h = pi_size[j][1];
      const int s = w * h * 3 / 2;

      if (t % s == 0)
        break;
    }
    if (j == i_max)
      t = 0;


    /* Try all size */
    for (j = 0; j < i_max; j++)
    {
      const int w = pi_size[j][0];
      const int h = pi_size[j][1];
      const int s = w * h * 3 / 2;
      double dd;

      int x, y, n;
      int64_t d;

      /* To small */
      if (i_read < 3 * s)
        continue;
      /* Check file size */
      if ((t > 0 && (t % s) != 0))
      {
        fprintf (stderr, "  * %dx%d ignored (incompatible file size)\n", w, h);
        continue;
      }


      /* We do a simple ssd between 2 consecutives lines */
      d = 0;
      for (n = 0; n < 3; n++)
      {
        uint8_t *p;

        /* Y */
        p = &pic[n * s];
        for (y = 0; y < h - 1; y++)
        {
          for (x = 0; x < w; x++)
            d += ssd (p[x] - p[w + x]);
          p += w;
        }

        /* U */
        p = &pic[n * s + w * h];
        for (y = 0; y < h / 2 - 1; y++)
        {
          for (x = 0; x < w / 2; x++)
            d += ssd (p[x] - p[(w / 2) + x]);
          p += w / 2;
        }

        /* V */
        p = &pic[n * s + 5 * w * h / 4];
        for (y = 0; y < h / 2 - 1; y++)
        {
          for (x = 0; x < w / 2; x++)
            d += ssd (p[x] - p[(w / 2) + x]);
          p += w / 2;
        }
      }
      dd = (double) d / (3 * w * h * 3 / 2);
      fprintf (stderr, "  * %dx%d d=%f\n", w, h, dd);

      if (dd < dbest)
      {
        i_best = j;
        dbest = dd;
      }
    }

    fseek (xyuv.yuv[i].f, 0, SEEK_SET);

    if (i_best < i_max)
    {
      fprintf (stderr, "  -> %dx%d\n", pi_size[i_best][0], pi_size[i_best][1]);
      *pi_width = pi_size[i_best][0];
      *pi_height = pi_size[i_best][1];
    }
  }

  free (pic);
}
